﻿#define _CRT_SECURE_NO_WARNINGS
//作者：宋安康
#include<stdio.h>
//int main()
//{
//	int n = 9;
//	float* pFloat = (float*)&n;
//	printf("n的值为：%d\n", n);
//	printf("*pFloat的值为：%f\n", *pFloat);
//	*pFloat = 9.0;
//	printf("num的值为：%d\n", n);
//	printf("*pFloat的值为：%f\n", *pFloat);
//	return 0;
//}
//有些浮点数在内存中无法精确保存
//double类型的精度比float更高
//两个浮点数比较大小的时候，直接使用==比较可能存在问题
//给一个精度
//0.000001
//if（abs(f - 5.6) < = 0.000001）

//int main()   //《C语言深度解剖》  --- B站蛋哥
//{
//	float a = 99.7f;
//	printf("%f\n",a);  //有些数字无法精确打印出来
//	return 0;
//}

//结构体
//结构体类型的声明
//struct stu {
//	char name[20];   //成员列表
//	int age;
//	char tell[20];
//	float score;
//}s1 ,s2, s3;  //创建的三个变量
//struct stu s4;// 创建的变量

//struct Stu
//{
//	char name[20];//名字
//	int age;//年龄
//	char sex[5];//性别
//	char id[20];//学号
//}; //分号不能丢

//struct Stu
//{
//	char name[20];//名字
//	int age;//年龄
//	char sex[5];//性别
//	char id[20];//学号
//};
//int main()
//{
//	//按照结构体成员的顺序初始化
//	struct Stu s = { "张三", 20, "男", "20230818001" };   //按照默认顺序初始化
//	printf("name: %s\n", s.name);
//	printf("age : %d\n", s.age);
//	printf("sex : %s\n", s.sex);
//	printf("id : %s\n", s.id);
//
//	//按照指定的顺序初始化
//	struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "女",.id = "990837" };   //按照指定顺序初始化
//	printf("name: %s\n", s2.name);
//	printf("age : %d\n", s2.age);
//	printf("sex : %s\n", s2.sex);
//	printf("id : %s\n", s2.id);
//	return 0;
//}

//结构的特殊声明
//在声明结构的时候，可以不完全的声明。

//匿名结构体类型
//只能使用一次
struct
{
	int a;
	char b;
	float c;
}x;
//int main() 
//{
//
//	return 0;
//}
//数据结构：描述的是数据在内存中的存储和组织结构
//结构的自引用
//struct   Node
//{
//	int data;   //存放数据
//	struct Node* next;  //存放写一个节点的地址
//};

struct   Node   //自引用不能写成匿名的
{
	int data;   //存放数据
	struct Node* next;  //存放写一个节点的地址
};
//int main()
//{
//
//	return 0;
//}

//结构体内存对⻬
//我们已经掌握了结构体的基本使⽤了。
//现在我们深⼊讨论⼀个问题：计算结构体的⼤⼩。
//这也是⼀个特别热⻔的考点： 结构体内存对⻬
struct s1
{
	char c1;
	char c2;
	int i;
};
struct s2
{
	char c1;
	int i;
	char c2;
	
};
//int main() 
//{
//	printf("%d\n",sizeof(struct s1));  //8
//	printf("%d\n",sizeof(struct s2));  //12
//	return 0;
//}//结构体内存对齐 ------  很重要

//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
//2. 其他成员变量要对⻬到某个数字（对⻬数）的整数倍的地址处。
//对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
//-VS 中默认的值为 8
//- Linux中 gcc 没有默认对⻬数，对⻬数就是成员⾃⾝的⼤⼩
//3. 结构体总⼤⼩为最⼤对⻬数（结构体中每个成员变量都有⼀个对⻬数，
// 所有对⻬数中最⼤的）的整数倍。
//4. 如果嵌套了结构体的情况，嵌套的结构体成员对⻬到⾃⼰的成员中最⼤
// 对⻬数的整数倍处，结构体的整体⼤⼩就是所有最⼤对⻬数（含嵌套结构体中成员的对⻬数）的整数倍。
//宏
//offsetof -- 计算结构体成员的偏移量
//#include<stddef.h>
//int main() 
//{
//	//练习1
//	struct S1
//	{
//		char c1;
//		int i;
//		char c2;
//	};
//	printf("%d\n", sizeof(struct S1));
//	printf("%zd\n",offsetof(struct S1, c2));   //计算偏移量offsetof()
//	//练习2
//	struct S2
//	{
//		char c1;
//		char c2;
//		int i;
//	};
//	printf("%d\n", sizeof(struct S2));
//	//练习3
//	struct S3
//	{
//		double d;//8
//		char c;//1
//		int i;//4
//	};
//	printf("%d\n", sizeof(struct S3));   //16
//	//练习4-结构体嵌套问题
//	struct S4
//	{
//		char c1;
//		struct S3 s3;
//		double d;
//	};
//	printf("%d\n", sizeof(struct S4));
//	printf("%zd\n", offsetof(struct S4, s3));   //计算偏移量offsetof()
//
//	return 0;
//}
//为什么存在内存对⻬?
//1. 平台原因(移植原因)：
//不是所有的硬件平台都能访问任意地址上的任意数据的；某些硬件平台只能在某些地址处取某些特定
//类型的数据，否则抛出硬件异常。
//2. 性能原因：
//数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于，为了访问未对⻬的内存，处理器需要
//作两次内存访问；⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节，则地
//址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数，那么就可以
//⽤⼀个内存操作来读或者写值了。否则，我们可能需要执⾏两次内存访问，因为对象可能被分放在两
//个8字节内存块中。
//总体来说：结构体的内存对⻬是拿空间来换取时间的做法。

//修改默认对⻬数
//#pragma 这个预处理指令，可以改变编译器的默认对⻬数。

//#pragma pack(1)//设置默认对⻬数为1-----直接放，不用对齐
//struct S
//{
//	char c1;
//	int i;
//	char c2;
//};
//#pragma pack()//取消设置的对⻬数，还原为默认8
//int main()
//{
//	//输出的结果是什么？
//	printf("%d\n", sizeof(struct S));
//	return 0;
//}

//结构体传参
struct S
{
	int data[1000];
	int num;
};

//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
	printf("%d\n", s.data[0]);
}
//结构体地址传参
void print2(const struct S* ps)   //更好，不用开辟新的空间，节省内存
{
	printf("%d\n", ps->num);
	printf("%d\n", ps->data[0]);
}
int main()
{
	struct S s = { {1,2,3,4,5}, 100 };  //初始化
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}